//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Text;
using System.Windows;
using System.Windows.Media;
using OpenLS.Core;

namespace OpenLS.Spreadsheet
{
	class CellTextRenderingInformation
	{
		public CellTextRenderingInformation(CellDisplayInfoKey cell, ColorMode mode)
		{
#if PERFORMANCE
			OpenLS.Spreadsheet.Performance.PerformanceData.Current.CellDisplayInfo.Start();
#endif
			this._key = cell;
			lastColumnWidth = cell.ActualCharacterWidth;
			Build(cell, mode);
#if PERFORMANCE
			OpenLS.Spreadsheet.Performance.PerformanceData.Current.CellDisplayInfo.Stop();
#endif
		}
		public CellTextRenderingInformation(CellDisplayInfoKey cell): this (cell, ColorMode.Color)
		{
		}

		readonly CellDisplayInfoKey _key;
		internal double lastColumnWidth;

		private void Build(CellDisplayInfoKey cell, ColorMode mode)
		{
			object fv = cell.GetRawFormattedValue();
			if (fv == null)
				return;
			if (fv is string)
			{
				var ss = (string)fv;
				double w;
				FormattedText = GetFormattedText(cell, mode, ss, null, out w);
				textWidth = w;
				if (cell.Format is GeneralFormat && cell.Value is double)
				{
					var i = (int) cell.ActualCharacterWidth; //\\tofix
					while (textWidth > cell.ActualWidth && i > 0)
					{
						i--;
						ss = (string)cell.GetRawFormattedValue(i);
						FormattedText = GetFormattedText(cell, mode, ss, null, out w);
						textWidth = w;
					}
				}
			}
			else if (fv is CompositeFormattedValue)
			{
				var compositeValue = (CompositeFormattedValue)fv;
				var leftText = new StringBuilder();
				StringBuilder rightText = null;
				StringBuilder currentText = leftText;
				List<int> currentIndices = leftIndices;
				_repeatText = null;
				foreach (object o in compositeValue.Items)
				{
					if (o is string)
					{
						currentText.Append((string)o);
						continue;
					}
					if (o is InvisibleText)
					{
						currentIndices.Add(currentText.Length);
						currentText.Append(((InvisibleText)o).Text);
						continue;
					}
					var rt = (RepeatText)o;
					rightText = new StringBuilder();
					rightIndices = new List<int>();
					currentText = rightText;
					currentIndices = rightIndices;
					_repeatText = rt.Text;
				}
				if (_repeatText != null)
				{
					double w;
					FormattedText = GetFormattedText(cell, mode, leftText.ToString(), leftIndices, out w);
					textWidth = w;
					FormattedText.TextAlignment = TextAlignment.Left;
					if (rightText.Length > 0)
					{
						_rightFormattedText = GetFormattedText(cell, mode, rightText.ToString(), rightIndices, out w);
						RightTextWidth = w;
						_rightFormattedText.TextAlignment = TextAlignment.Right;
					}
				}
				else
				{

					double w;
					FormattedText = GetFormattedText(cell, mode, leftText.ToString(), leftIndices, out w);
					textWidth = w;
				   //\\ this.formattedText.TextAlignment = TextAlignment.Left;
				}

			}
			else
			{
				throw new NotImplementedException();
			}
			if (cell.Rotation != 0)
			{
				var group = new TransformGroup();
				double xx = TextHeight / 2;
				group.Children.Add(new TranslateTransform(-xx, -xx));
				group.Children.Add(new RotateTransform(-cell.Rotation));
				group.Children.Add(new TranslateTransform(xx, xx));
				_transform = group;
			}
		}
		internal double TotalTextWidth
		{
			get
			{
				return textWidth + RightTextWidth;
			}
		}


		private FormattedText _formattedText;

		public FormattedText FormattedText
		{
			get { return _formattedText; }
			private set
			{
				Contract.Requires(value != null);
				_formattedText = value;
			}
		}
	
		public double textWidth;

		private FormattedText _rightFormattedText;
		public FormattedText RightFormattedText { get { return _rightFormattedText; } }
		internal double RightTextWidth;


		double? _textHeight;
		public double TextHeight
		{
			get
			{
#if PERFORMANCE
				OpenLS.Spreadsheet.Performance.PerformanceData.Current.TextHeight.Start();
#endif
				if (_textHeight == null)
				{
					if (_key.TextWrapping != TextWrapping.NoWrap)
					{
						var ft = CreateFormattedText(FormattedText.Text, _key, Brushes.Black);
						ft.MaxTextWidth = _key.ActualWidth;

						return ft.Height; // +ft.LineHeight;
					}
					_textHeight = FormattedText.Height;
					if (_rightFormattedText != null)
						_textHeight = Math.Max(_textHeight.Value, _rightFormattedText.Height);
				}
#if PERFORMANCE
				OpenLS.Spreadsheet.Performance.PerformanceData.Current.TextHeight.Stop();
#endif
				return _textHeight.Value;
			}
		}
		static FormattedText GetFormattedText(CellDisplayInfoKey cell, ColorMode mode, string ss, IEnumerable<int> invisibleIndices, out double width)
		{
			Contract.Ensures(Contract.Result<FormattedText>() != null);
			if (cell.IsVertical)
			{
				var sb = new StringBuilder();
				foreach (char c in ss)
				{
					sb.Append(c);
					sb.Append('\n');
				}
				ss = sb.ToString();
			}
			Brush foreground = GetForegroundBrush(cell.Cell, mode);
			var ft = CreateFormattedText(ss, cell,
										 foreground);
			if (cell.TextWrapping == TextWrapping.NoWrap && !cell.IsVertical)
				ft.MaxLineCount = 1;
			TextDecorationCollection col;
			switch (cell.ActualFontUnderline)
			{
				case UnderlineType.None:
					break;
				case UnderlineType.Single:
					col = new TextDecorationCollection
							  {
								  new TextDecoration(TextDecorationLocation.Underline,
													 new Pen(foreground, 1), 0,
													 TextDecorationUnit.FontRecommended, TextDecorationUnit.Pixel)
							  };
					ft.SetTextDecorations(col);
					break;
				case UnderlineType.Double:
					col = new TextDecorationCollection
							  {
								  new TextDecoration(TextDecorationLocation.Underline,
													 new Pen(foreground, 1), 0,
													 TextDecorationUnit.FontRecommended, TextDecorationUnit.Pixel),
								  new TextDecoration(TextDecorationLocation.Underline,
													 new Pen(foreground, 1), 1,
													 TextDecorationUnit.FontRecommended, TextDecorationUnit.Pixel)
							  };
					ft.SetTextDecorations(col);
					break;
				case UnderlineType.SingleAccounting:
					col = new TextDecorationCollection
							  {
								  new TextDecoration(TextDecorationLocation.Underline,
													 new Pen(foreground, 1), 1,
													 TextDecorationUnit.FontRecommended, TextDecorationUnit.Pixel)
							  };
					ft.SetTextDecorations(col);
					break;
				case UnderlineType.DoubleAccounting:
					col = new TextDecorationCollection
							  {
								  new TextDecoration(TextDecorationLocation.Underline,
													 new Pen(foreground, 1), 1,
													 TextDecorationUnit.FontRecommended, TextDecorationUnit.Pixel),
								  new TextDecoration(TextDecorationLocation.Underline,
													 new Pen(foreground, 1), 2,
													 TextDecorationUnit.FontRecommended, TextDecorationUnit.Pixel)
							  };
					ft.SetTextDecorations(col);
					break;
			}
			width = ft.Width;
			ft.TextAlignment = cell.TextAlignment;
			SetInvisibleIndices(ft, invisibleIndices);
			return ft;
		}

		internal static Brush GetForegroundBrush(Cell cell, ColorMode mode)
		{
			switch (mode)
			{
				case ColorMode.BlackAndWhite:
					return Brushes.Black;
				default:

					return ColorBrushManager.GetBrush(cell.ActualForeground, cell.Worksheet.Workbook.ColorScheme);
			}
		}
		private Transform _transform;
		private string _repeatText
			;

		private List<int> leftIndices = new List<int>();
		private List<int> rightIndices;
		public bool HasRepeatText { get { return _repeatText != null; } }

		static FormattedText CreateFormattedText (string text, CellDisplayInfoKey cell, Brush foreground)
		{
			Contract.Ensures(Contract.Result<FormattedText>()!= null);
			return new FormattedText(text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, cell.Typeface,
									 cell.ActualFontSizeInPixel,
									 foreground){Trimming = TextTrimming.None};
		}
		public FormattedText GetRepeatText (double maxWidth, CellDisplayInfoKey cell, ColorMode colorMode )
		{
			Contract.Requires(HasRepeatText);
			FormattedText result = null;
			StringBuilder sb = new StringBuilder();
			var foreground = GetForegroundBrush(cell.Cell, colorMode);
			string leftText = FormattedText.Text;
			string rightText = RightFormattedText != null ? RightFormattedText.Text : "";
			int repeatCount = 0;
			for (int i = 0; i < 1000; i++)
			{
				FormattedText candidate = CreateFormattedText(leftText + sb + rightText, cell, foreground);
				if (candidate.Width > maxWidth)
				{
					if (repeatCount > 0) repeatCount--;
						SetInvisibleIndices(result, Enumerable.Union(leftIndices, rightIndices != null ? Enumerable.Select(rightIndices, ii => ii + leftText.Length + repeatCount) : new int[0]));
					return result;
				}
				result = candidate;
				sb.Append(_repeatText);
				repeatCount++;
			}
			if (repeatCount > 0)
				repeatCount--;
			SetInvisibleIndices(result, Enumerable.Union(leftIndices, rightIndices !=null? Enumerable.Select(rightIndices, ii => ii + leftText.Length + repeatCount):new int[0]));
			return result;
		}

		static void SetInvisibleIndices (FormattedText formattedText, IEnumerable<int> indexes)
		{
			if (indexes == null)
				return;
			foreach (int i in indexes)
				formattedText.SetForegroundBrush(null, i, 1);
		}
		internal Transform Transform
		{
			get { return _transform; }
		}

		public Cell Cell
		{
			get { return _key.Cell; }
		}
		public CellDisplayInfoKey Key
		{
			get
			{
				return _key;
			}
		}

		public bool IsSpecial       
		{
			get { return RightFormattedText != null || this.HasRepeatText; }
		}
	}
}
